<?php

Raise::load('core.RaiseTemplateUIControl');
Raise::load('core.RaiseApplication');
Raise::load('core.RaiseCollection');
Raise::load('core.RaisePath');
Raise::load('control.RaiseAjaxArea');
Raise::load('core.RaiseKeyValuePair');
Raise::load('core.RaiseHtml');
Raise::load('core.RaiseAppPackageLoader');
Raise::load('control.RaiseFormErrorLabel');

/**
 * RaisePage abstract Class
 * defines an abstract page in phpRaise
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.UI
 * @since 1.2
 */
abstract class RaisePage extends RaiseTemplateUIControl {

    /**
     * File name of this page
     * @var string
     */
    protected $filename;
    /**
     * Title of this page (can be used as <title>)
     * @var string
     */
    protected $title;
    /**
     * The RaiseApplication that started this RaisePage
     * @var RaiseApplication
     */
    protected $application;
    /**
     * Collection of parameters passed into the page
     * @var RaiseCollection
     */
    protected $params;
    /**
     * The postback controllers that this page will be using
     * @var array|RaiseCollection|string|RaiseString
     */
    protected $usePostback;
    /**
     * The model or collection of models that will be used to automatically fill up fields on the page
     * @var RaiseDbModel|RaiseCollection
     */
    protected $model;

    /**
     * Constructs a new RaisePage
     * @param string $t Template name or template string to use
     * @param RaiseCollection|array $p (optional) A collection of parameters passed into the page
     */
    function __construct($t = null, $p = null) {
        if ($t === null) {
            // no template provided, search for default template with same name
            $t = get_class($this);
        }
        parent::__construct($t);
        $this->filename = RaisePath::baseName($_SERVER['PHP_SELF']);
        if (is_array($p)) {
            $p = new RaiseCollection($p);
        }

        if ($p instanceof RaiseCollection) {
            $this->params = $p;
        } else {
            $this->params = new RaiseCollection();
        }
    }

    /**
     * Get the collection of parameters passed into this page
     * @return RaiseCollection
     */
    public function params() {
        return $this->params;
    }

    /**
     * Get or set the title of the page
     * @param string $t (optional) If set, the page title will be set to the new value.
     * @return string
     */
    public function title($t = null) {
        if (func_num_args() == 1) {
            $this->title = $t;
        }
        return $this->title;
    }

    /**
     * Get or set the RaiseApplication that started this page
     * @param RaiseApplication $app (optional) If set, the new value will be set.
     * @return RaiseApplication
     */
    public function application($app = null) {
        if (func_num_args() == 1) {
            if (!($app instanceof RaiseApplication)) {
                throw new RaiseInvalidArgumentException('RaisePage::application() expecting $app to be an instance of RaiseApplication, ' . RaiseVariable::typeInfo($app) . ' given instead.');
            }
            $this->application = $app;
        }
        return $this->application;
    }

    /**
     * Called upon when the page is preparing
     */
    public function load() {
        $this->application->plugin()->callHook('raise.page.load', $this);
        $this->application->response()->headers()->add('Content-Type', 'text/html; charset=UTF-8');

        $collection = new RaiseCollection();
        if (RaiseVariable::isType($this->usePostback, array('RaiseString', 'string'))) {
            $collection->add($this->usePostback);
        } elseif (RaiseVariable::isType($this->usePostback, array('RaiseCollection', 'array'))) {
            $collection = $this->usePostback;
        }

        foreach ($collection as $c) {
            $c = $c . 'Postback';
            $path = RaisePath::combine($this->application()->rootPath(), 'raise' . RaisePath::directorySeparator() . 'postback' . RaisePath::directorySeparator() . $c . '.php');
            RaiseAppPackageLoader::loadClass($path);
            $c::prepare($this->application());
        }
    }

    /**
     * The building of the page.
     * This is where you add all your controls and modify the view of the page.
     * Method is named as 'build' instead of 'construct' to prevent the confusion
     * between PHP object instantiation and the page construction method.
     * @abstract
     */
    abstract function build();

    /**
     * Called when the page is output already
     * and the application is ending execution.
     */
    public function unload() {
        $this->application->plugin()->callHook('raise.page.unload', $this);
    }

    /**
     * Build and prepare output into the response
     */
    public function output() {
        $this->application->plugin()->callHook('raise.page.build', $this);

        $output = '';
        //ob_start();
        $this->build();
        //$output = ob_get_contents();
        //ob_end_clean();

        if ($this->model instanceof RaiseDbModel) {
            $name = get_class($this->model);
            foreach ($this->model as $k => $v) {
                $this->fields()->add($name . '.' . $k, $v);
            }
        } elseif ($this->model instanceof RaiseCollection) {
            foreach ($this->model as $name => $model) {
                if($model instanceof RaiseCollection){
                    foreach($model as $m){
                        foreach ($m as $k => $v) {
                            $this->fields()->add($name . '.' . $k, $v);
                        }
                    }
                }elseif($model instanceof RaiseDbModel){
                    foreach ($model as $k => $v) {
                        $this->fields()->add($name . '.' . $k, $v);
                    }
                }
            }
        }

        if ($this->params()->keyExists('ajaxcaller')) {
            $this->application->plugin()->callHook('raise.page.prepareAjax.start', $this);
            $id = $this->params()->get('ajaxcaller');
            $control = $this->findRaiseAjaxAreaById($id);
            if ($control != null && $control instanceof RaiseAjaxArea) {
                $control->isAjax(true);
                $this->application()->response()->headers()->add('Content-Type', 'text/javascript');
                $this->application->plugin()->callHook('raise.page.prepareAjax.end.success', $this);
                return $control;
            } else {
                $this->application->plugin()->callHook('raise.page.prepareAjax.end.fail', $this);
                throw new RaiseInvalidRequestException('The RaiseAjaxArea control of $clientId \'' . $id . '\' was not found.');
            }
        } else {
            $this->application->plugin()->callHook('raise.page.view.prepare.start', $this);
            $this->fields()->add('page.title', $this->title());
            $this->fields()->add('page.doctype', RaiseHtml::$selectedDocType);
            $this->fields()->add('page.baseurl', $this->application()->settings()->get('phpRaise', 'baseUrl'));
            if (!$this->fields()->get('page.content')) {
                //$this->fields()->add('page.content', $this->fields()->get('page.content'));
            //} else {
                $this->fields()->add('page.content', $output);
            }

            foreach($this->fields() as $v){
                if($v instanceof RaiseUIControl){
                    $v->page($this);
                }
            }
            $html = parent::output();

            $this->application->plugin()->callHook('raise.page.view.prepare.end', $this);
            return $html;
        }

        /**
         * The postback feedback is cleared after each page load
         */
        $this->application->session()->remove(RaisePostbackFeedback::SESSION_KEY);
    }

    /**
     * Find a RaiseAjaxArea by its client ID
     * @param string $id The ID of the RaiseAjaxArea
     * @return RaiseAjaxArea
     */
    private function findRaiseAjaxAreaById($id){
        foreach($this->fields() as $v){
            if($v instanceof RaiseAjaxArea && $v->clientId() == $id){
                return $v;
            }
        }
        return null;
    }

}